﻿using Examination.Args;
using Examination.Args.Request;
using Examination.Filter;
using Examination.Model;
using Examination.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Examination.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    [TypeFilter(typeof(WebVerificationFilterAttribute))]
    public class ExamController : ControllerBase
    {
        private readonly DataContext _dataContext;

        public ExamController(DataContext dataContext) => _dataContext = dataContext;

        [HttpPost]
        public async Task<IActionResult> Paper([FromQuery] long id, [FromServices] IArgsExtend argsExtend)
        {
            User user = ((ArgsExtend)argsExtend).User;
            Model.Examination examination = await _dataContext.Examinations.FirstOrDefaultAsync(e => e.Id == id);
            if (examination == null)
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "比赛不存在" });
            }
            var papers = await _dataContext.Papers.Where(p => p.ExaminationId == examination.Id && p.UserId == user.Id).OrderByDescending(p => p.StartTime).ToListAsync();
            if (papers.FirstOrDefault()?.EndTime == new DateTime())
            {
                Paper paper = papers.FirstOrDefault();
                if ((DateTime.Now - paper.StartTime).TotalMinutes > examination.Time || DateTime.Now > examination.EndTime)
                {
                    paper.EndTime = DateTime.Now;
                    _dataContext.Papers.Update(paper);
                    await _dataContext.SaveChangesAsync();
                    if (papers.Count >= examination.Times)
                    {
                        return new JsonResult(new ResponseData() { Status = 1, Msg = "比赛已结束" });
                    }
                }
                else
                {
                    var paperQuestions = await _dataContext.PaperQuestions.Where(pq => pq.PaperId == paper.Id).ToListAsync();
                    long[] qids = paperQuestions.Select(p => p.QuestionId).ToArray();
                    var qs = await _dataContext.Questions.Where(q => qids.Contains(q.Id)).Select(q => new { q.Id, q.Title, q.Type, q.Option1, q.Option2, q.Option3, q.Option4 }).ToListAsync();
                    var questions = paperQuestions.Join(qs, pq => pq.QuestionId, q => q.Id, (pq, q) => new { pq.Id, pq.Answer, pq.OptionsOrder, questionId = q.Id, q.Title, q.Type, q.Option1, q.Option2, q.Option3, q.Option4 }).ToList();
                    return new JsonResult(new ResponseData() { Data = new { examination, paper, questions } });
                }
            }
            if (papers.Count < examination.Times)
            {
                Paper paper = new Paper()
                {
                    ExaminationId = examination.Id,
                    UserId = user.Id,
                    StartTime = DateTime.Now
                };
                await _dataContext.Papers.AddAsync(paper);
                await _dataContext.SaveChangesAsync();
                List<PaperQuestion> paperQuestions = new List<PaperQuestion>();
                var eqs = await _dataContext.ExamQuestions.Where(eq => eq.ExaminationId == examination.Id).Join(_dataContext.Questions, eq => eq.QuestionId, q => q.Id, (eq, q) => new { eq.Id, eq.QuestionId, q.Type }).ToListAsync();
                long[] mOptionIds = eqs.Where(eq => eq.Type == QuestionType.Radio || eq.Type == QuestionType.Multiple).Select(eq => eq.QuestionId).ToArray();
                if (examination.RandomQuestions)
                {
                    Random random = new Random();
                    paperQuestions.AddRange(eqs.Where(eq => eq.Type == QuestionType.Fill).OrderBy(eq => random.NextDouble()).Select(eq => new PaperQuestion() { PaperId = paper.Id, QuestionId = eq.QuestionId, }).Take(examination.FillCount).ToList());
                    paperQuestions.AddRange(eqs.Where(eq => eq.Type == QuestionType.Judge).OrderBy(eq => random.NextDouble()).Select(eq => new PaperQuestion() { PaperId = paper.Id, QuestionId = eq.QuestionId, }).Take(examination.JudgeCount).ToList());
                    paperQuestions.AddRange(eqs.Where(eq => eq.Type == QuestionType.Radio).OrderBy(eq => random.NextDouble()).Select(eq => new PaperQuestion() { PaperId = paper.Id, QuestionId = eq.QuestionId, }).Take(examination.RadioCount).ToList());
                    paperQuestions.AddRange(eqs.Where(eq => eq.Type == QuestionType.Multiple).OrderBy(eq => random.NextDouble()).Select(eq => new PaperQuestion() { PaperId = paper.Id, QuestionId = eq.QuestionId, }).Take(examination.MultipleCount).ToList());
                }
                else
                {
                    paperQuestions.AddRange(eqs.Select(eq => new PaperQuestion() { PaperId = paper.Id, QuestionId = eq.QuestionId, }).ToList());
                }
                if (examination.RadioCount + examination.MultipleCount > 0 && examination.RandomOptions)
                {
                    for (int i = 0; i < paperQuestions.Count; i++)
                    {
                        paperQuestions[i].OptionsOrder = String.Join(',', ArrayHelper.RandomArray());
                    }
                }
                await _dataContext.PaperQuestions.AddRangeAsync(paperQuestions);
                await _dataContext.SaveChangesAsync();
                long[] qids = paperQuestions.Select(p => p.QuestionId).ToArray();
                var qs = await _dataContext.Questions.Where(q => qids.Contains(q.Id)).Select(q => new { q.Id, q.Title, q.Type, q.Option1, q.Option2, q.Option3, q.Option4 }).ToListAsync();
                var questions = paperQuestions.Join(qs, pq => pq.QuestionId, q => q.Id, (pq, q) => new { pq.Id, pq.Answer, pq.OptionsOrder, questionId = q.Id, q.Title, q.Type, q.Option1, q.Option2, q.Option3, q.Option4 }).ToList();
                return new JsonResult(new ResponseData() { Data = new { examination, paper, questions } });
            }
            else
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "比赛已结束" });
            }
        }

        [HttpPost]
        public async Task<IActionResult> Answer([FromBody] AnswerParameter parameter, [FromServices] IArgsExtend argsExtend)
        {
            User user = ((ArgsExtend)argsExtend).User;
            PaperQuestion paperQuestion = await _dataContext.PaperQuestions.FirstOrDefaultAsync(p => p.Id == parameter.PaperQuestionId);
            if (paperQuestion == null)
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "答案提交失败，考题不存在" });
            }
            Paper paper = await _dataContext.Papers.FirstOrDefaultAsync(p => p.Id == paperQuestion.PaperId && p.UserId == user.Id);
            if (paper == null)
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "答案提交失败，试卷不存在" });
            }
            Model.Examination examination = await _dataContext.Examinations.FirstOrDefaultAsync(e => e.Id == paper.ExaminationId);
            if (examination == null)
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "答案提交失败，比赛不存在" });
            }
            if (paper.EndTime != new DateTime())
            {
                return new JsonResult(new ResponseData() { Status = 2, Msg = "答案提交失败，比赛已结束" });
            }
            if ((DateTime.Now - paper.StartTime).TotalMinutes > examination.Time || DateTime.Now > examination.EndTime)
            {
                paper.EndTime = DateTime.Now;
                _dataContext.Papers.Update(paper);
                await _dataContext.SaveChangesAsync();
                return new JsonResult(new ResponseData() { Status = 2, Msg = "答案提交失败，比赛已结束" });
            }
            Question question = await _dataContext.Questions.FirstOrDefaultAsync(q => q.Id == paperQuestion.QuestionId);
            if (question == null)
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "答案提交失败，考题不存在" });
            }
            paperQuestion.Answer = parameter.Answer;
            switch (question.Type)
            {
                case QuestionType.Judge:
                    paperQuestion.Score = paperQuestion.Answer.Equals(question.Answer) ? examination.JudgeScore : 0;
                    break;
                case QuestionType.Radio:
                    paperQuestion.Score = paperQuestion.Answer.Equals(question.Answer) ? examination.RadioScore : 0;
                    break;
                case QuestionType.Multiple:
                    paperQuestion.Score = paperQuestion.Answer.Equals(question.Answer) ? examination.MultipleScore : 0;
                    break;
                case QuestionType.Fill:
                    if ("true".Equals(question.Option1))
                    {
                        Regex regex = new Regex(question.Answer, "true".Equals(question.Option2) ? RegexOptions.None : RegexOptions.IgnoreCase);
                        paperQuestion.Score = regex.IsMatch(paperQuestion.Answer) ? examination.FillScore : 0;
                    }
                    else if ("true".Equals(question.Option2))
                    {
                        paperQuestion.Score = paperQuestion.Answer.Equals(question.Answer) ? examination.FillScore : 0;
                    }
                    else
                    {
                        paperQuestion.Score = paperQuestion.Answer.ToLower().Equals(question.Answer.ToLower()) ? examination.FillScore : 0;
                    }
                    break;
                default:
                    break;
            }
            paperQuestion.AnswerTime = DateTime.Now;
            _dataContext.PaperQuestions.Update(paperQuestion);
            await _dataContext.SaveChangesAsync();
            paper.Score = await _dataContext.PaperQuestions.Where(pq => pq.PaperId == paper.Id).SumAsync(pq => pq.Score);
            _dataContext.Papers.Update(paper);
            await _dataContext.SaveChangesAsync();
            return new JsonResult(new ResponseData());
        }

        [HttpPost]
        public async Task<IActionResult> HandPaper([FromQuery] long id, [FromServices] IArgsExtend argsExtend)
        {
            User user = ((ArgsExtend)argsExtend).User;
            Paper paper = await _dataContext.Papers.FirstOrDefaultAsync(p => p.Id == id && p.UserId == user.Id);
            if (paper == null)
            {
                return new JsonResult(new ResponseData() { Status = 1, Msg = "交卷失败，试卷不存在" });
            }
            System.Diagnostics.Debug.WriteLine(paper.EndTime);
            System.Diagnostics.Debug.WriteLine(new DateTime());
            if (paper.EndTime == new DateTime())
            {
                paper.Score = await _dataContext.PaperQuestions.Where(pq => pq.PaperId == paper.Id).SumAsync(pq => pq.Score);
                paper.EndTime = DateTime.Now;
                _dataContext.Papers.Update(paper);
                await _dataContext.SaveChangesAsync();
            }
            return new JsonResult(new ResponseData());
        }
    }
}
